package utils

import (
	"crypto"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
)

// Sha256 sha256带盐加密
func Sha256(text, salt string) string {
	str := fmt.Sprintf("%s%s", text, salt)
	return base64.RawURLEncoding.EncodeToString(sha256.New().Sum([]byte(str)))
}

// EncryptAesWithCbc AES加密
func EncryptAesWithCbc(in []byte, key []byte, iv []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	en := cipher.NewCBCEncrypter(block, iv)
	out := make([]byte, len(in))
	copy(out, in)
	en.CryptBlocks(out, out)
	return out, nil
}

// DecryptAesWithCbc AES解密
func DecryptAesWithCbc(in []byte, key []byte, iv []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	de := cipher.NewCBCDecrypter(block, iv)
	out := make([]byte, len(in))
	copy(out, in)
	de.CryptBlocks(out, out)
	return out, nil
}

// Base64Encode base64编码
func Base64Encode(text string) string {
	return base64.StdEncoding.EncodeToString([]byte(text))
}

// Base64Decode base64解码
func Base64Decode(text string) (string, error) {
	result, err := base64.StdEncoding.DecodeString(text)
	if err != nil {
		return "", err
	}
	return string(result), nil
}

// RsaEncryptBase64 使用 RSA 公钥加密数据, 返回加密后并编码为 base64 的数据
func RsaEncryptBase64(originalData, publicKey string) (string, error) {
	block, _ := pem.Decode([]byte(publicKey))
	pubKey, parseErr := x509.ParsePKIXPublicKey(block.Bytes)
	if parseErr != nil {
		fmt.Println(parseErr)
		return "", errors.New("解析公钥失败")
	}
	encryptedData, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey.(*rsa.PublicKey), []byte(originalData))
	return base64.StdEncoding.EncodeToString(encryptedData), err
}

// RsaDecryptBase64 使用 RSA 私钥解密数据
func RsaDecryptBase64(encryptedData, privateKey string) (string, error) {
	encryptedDecodeBytes, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return "", err
	}
	block, _ := pem.Decode([]byte(privateKey))
	priKey, parseErr := x509.ParsePKCS8PrivateKey(block.Bytes)
	if parseErr != nil {
		fmt.Println(parseErr)
		return "", errors.New("解析私钥失败")
	}

	originalData, encryptErr := rsa.DecryptPKCS1v15(rand.Reader, priKey.(*rsa.PrivateKey), encryptedDecodeBytes)
	return string(originalData), encryptErr
}

// originalData 签名前的原始数据
// privateKey RSA 私钥
func signBase64(originalData, privateKey string) (string, error) {
	block, _ := pem.Decode([]byte(privateKey))
	priKey, parseErr := x509.ParsePKCS8PrivateKey(block.Bytes)
	if parseErr != nil {
		fmt.Println(parseErr)
		return "", errors.New("解析私钥失败")
	}

	// sha256 加密方式，必须与 下面的 crypto.SHA256 对应
	// 例如使用 sha1 加密，此处应是 sha1.New()，对应 crypto.SHA1
	hash := sha256.New()
	hash.Write([]byte(originalData))
	signature, err := rsa.SignPSS(rand.Reader, priKey.(*rsa.PrivateKey), crypto.SHA256, hash.Sum(nil), nil)

	return base64.StdEncoding.EncodeToString(signature), err
}

// VerySignWithBase64 RSA验签
// originalData 签名前的原始数据
// signData Base64 格式的签名串
// pubKey 公钥（需与加密时使用的私钥相对应）
// 返回 true 代表验签通过，反之为不通过
func VerySignWithBase64(originalData, signData, pubKey string) (bool, error) {
	sign, err := base64.StdEncoding.DecodeString(signData)
	if err != nil {
		return false, err
	}

	block, _ := pem.Decode([]byte(pubKey))
	pub, err1 := x509.ParsePKIXPublicKey(block.Bytes)
	if err1 != nil {
		return false, err1
	}
	// sha256 加密方式，必须与 下面的 crypto.SHA256 对应
	// 例如使用 sha1 加密，此处应是 sha1.New()，对应 crypto.SHA1
	hash := sha256.New()
	hash.Write([]byte(originalData))
	verifyErr := rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA256, hash.Sum(nil), sign)
	return verifyErr == nil, nil
}

// GenRsaKey 生成RSA公钥、私钥
// bits 生成的公私钥对的位数，一般为 1024 或 2048
// privateKey 生成的私钥
// publicKey 生成的公钥
func GenRsaKey(bits int) (privateKey, publicKey string) {
	priKey, err2 := rsa.GenerateKey(rand.Reader, bits)
	if err2 != nil {
		panic(err2)
	}

	derStream := x509.MarshalPKCS1PrivateKey(priKey)
	block := &pem.Block{
		Type:  "PRIVATE KEY",
		Bytes: derStream,
	}
	prvKey := pem.EncodeToMemory(block)
	puKey := &priKey.PublicKey
	derPkix, err := x509.MarshalPKIXPublicKey(puKey)
	if err != nil {
		panic(err)
	}
	block = &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: derPkix,
	}
	pubKey := pem.EncodeToMemory(block)

	privateKey = string(prvKey)
	publicKey = string(pubKey)
	return
}

// EncryptionSha256 按 {password}{salt} 格式进行sha256加密，返回 base64 格式字符串
func EncryptionSha256(password, salt string) string {
	str := fmt.Sprintf("%s%s", password, salt)
	return base64.RawURLEncoding.EncodeToString(sha256.New().Sum([]byte(str)))
}
